



"""三相可控硅板程序，对应板号PTKSXTYV1.0,程序版本号：V2版本，
V2:提升保护速率
V5：PID修改，增加旁路保护功能功能，适配有检修旁路的调压器
V5.2旁路开关错误判定，取消旁路故障后可控硅调压，而是检修旁路,磁保持继电器修改为接触器
V5.2.2新增对可控硅失控的判断，修改过流恢复值减小0.5A*变比，新增电操状态报警，删除接触器反馈报警
V5.2.3 新增不平衡保护功能
V5.3 新增断路器拒动保护，修正可控硅故障误报
V5.4 提高不平衡保护阈值
"""
from libs.umodbus.serial import Serial as ModbusRTUMaster
from libs.Voltage_regulator import PID
from machine import Pin,WDT,UART,SoftSPI,PWM
from libs.HT7036 import HT7036
import utime,dht,uasyncio
from libs.FirmwareUpdater import FirmwareUpdater
"""ESP32端口"""
# region
uart0=UART(0,baudrate=57600,tx=37,rx=39,timeout=200,rxbuf=4096)
host=ModbusRTUMaster(pins=(Pin(17),Pin(18)),baudrate=9600,data_bits=8,stop_bits=1,parity=None,uart_id=1)
spi=SoftSPI(baudrate=10000000, polarity=0, phase=1, bits=8, sck=Pin(9), mosi=Pin(11), miso=Pin(12))
cs1 = Pin(7, Pin.OUT,value=1)
cs2=Pin(5, Pin.OUT,value=1)
set_pin=Pin(15,Pin.IN,Pin.PULL_UP)
fan=Pin(3,Pin.OUT)
bypass_Control_close_A=Pin(2,Pin.OUT)
bypass_Control_open_A=Pin(1,Pin.OUT,value=1)
bypass_Control_close_B=Pin(38,Pin.OUT,value=1)
bypass_Control_open_B=Pin(4,Pin.OUT)
bypass_Control_close_C=Pin(21,Pin.OUT,value=1)
bypass_Control_open_C=Pin(36,Pin.OUT)
bypass_status_on={'A':0, 'B': 0, 'C': 0}
bypass_status_off={'A':0, 'B': 0, 'C': 0}
duty={'A':0, 'B': 0, 'C': 0}
thyristor_control_A=PWM(Pin(35),freq=1000,duty=0)
thyristor_control_B=PWM(Pin(33),freq=1000,duty=0)
thyristor_control_C=PWM(Pin(16),freq=1000,duty=0)
DHT11= dht.DHT11(Pin(14))
real_temperature=0
temperature_start_time=0
temperature_duration_time=0
reporting_start_time=0
reporting_duration_time=0
first_run_time = None
second_run_time = None
seting_HT7036 = 0
adjust_times=0
FirmwareUpdater_data = ''
version=570
modbus_disconnection=0
unbalanced_voltage_1_start_time=0
unbalanced_voltage_1_duration=0
unbalanced_voltage_2_start_time=0
unbalanced_voltage_2_duration=0
unbalanced_voltage_restore_start_time=0
unbalanced_voltage_restore_duration=0
updata_start_time = 0
updata_duration = 0
UP_data=b''
def ac_sampling():
    """交流采样换算值功能"""
    Ua1, Ia1, *_ = IC1.read_data('A')
    Ub1, Ib1, *_ = IC1.read_data('B')
    Uc1, Ic1, *_ = IC1.read_data('C')
    Ua, Ia, *_ = IC2.read_data('A')
    Ub, Ib, *_ = IC2.read_data('B')
    Uc, Ic, *_ = IC2.read_data('C')
    U_data_in = {'A': Ua, 'B':Ub, 'C':Uc}
    I_data_in = {'A': Ia, 'B':Ib, 'C':Ic}
    U_data_out = {'A': Ua1, 'B':Ub1, 'C':Uc1}
    I_data_out = {'A': Ia1, 'B':Ib1, 'C':Ic1}
    return U_data_in,I_data_in,U_data_out,I_data_out
def pwm_controls(pwm,phase):
    if phase=='A':
        thyristor_control_A.duty(int(pwm['A']))
    if phase=='B':
        thyristor_control_B.duty(int(pwm['B']))
    if phase=='C':
        thyristor_control_C.duty(int(pwm['C']))
def Controls(gear_position,pwm,phase):
    """控制函数：旁路控制，检修控制，调压PWM输出函数"""
    if gear_position[phase]==0:
        if gear_position[phase] == 0:
            bypass_status_on[phase] += 1
            if bypass_status_on[phase] == 1:
                pwm[phase]=300
                pwm_controls(pwm,phase)
                utime.sleep_ms(200)
                pwm[phase]=100
                pwm_controls(pwm,phase)
                utime.sleep_ms(200)
                Contactor_control(0,phase)
                utime.sleep_ms(200)
                pwm[phase]=0
                pwm_controls(pwm,phase)
                bypass_status_off[phase] = 0
    elif gear_position[phase]==1:
        bypass_status_off[phase] += 1
        if bypass_status_off[phase] == 1:
            if phase == 'A':
                pid.pid_a.set_initial_value(None,None,None) # 重置PID控制器
                pid.pwm_value_old['A']=0
            if phase == 'B':
                pid.pid_b.set_initial_value(None,None,None)
                pid.pwm_value_old['B'] = 0
            if phase == 'C':
                pid.pid_c.set_initial_value(None,None,None)
                pid.pwm_value_old['C'] = 0
            Contactor_control(1, phase)
            bypass_status_on[phase] = 0
            utime.sleep_ms(50)
            pwm_controls(pwm, phase)
        else:
            pwm_controls(pwm, phase)
def reckon_by_time(start_time):
    if start_time == 0:
        start_time = utime.time()
        duration = 0
    else:
        duration = utime.time() - start_time
    return duration, start_time
def Contactor_control(state,phase):
    if phase=='A':
        if state ==0:
            bypass_Control_open_A.value(1)
        else:
            bypass_Control_open_A.value(0)
    elif phase=='B':
        if state ==0:
            bypass_Control_open_B.value(1)
        else:
            bypass_Control_open_B.value(0)
    elif phase=='C':
        if state ==0:
            bypass_Control_open_C.value(1)
        else:
            bypass_Control_open_C.value(0)
def power_on():
    thyristor_control_A.duty(0)
    thyristor_control_B.duty(0)
    thyristor_control_C.duty(0)
    utime.sleep_ms(500)
    for phase in ['A', 'B', 'C']:
        Contactor_control(0,phase)
        utime.sleep_ms(500)
def log_error(e):
    "故障日志输出"
    with open('error.log', 'a') as f: #打开故障日志为追加模式
        year, month, day, hour, minute, second, weekday, yearday = utime.localtime()[:8]
        current_time = "{:04d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}".format(year, month, day, hour, minute, second)
        #时间戳设置
        f.write('{} Error: {}\n'.format(current_time, e)) #格式化写入的格式，错误类型，错误值，时间戳
def setting_up():
    global temperature_duration_time,temperature_start_time,seting_HT7036
    temperature_data=1
    try:
        IC1.power_on()
        IC2.power_on()
    except:
        pass
    utime.sleep(5)
    while True:
        try:
            U_data_in, I_data_in, U_data_out, I_data_out= ac_sampling()
            screen_data_update= [int(U_data_in['A']* 100), int(U_data_in['B'] * 100), int(U_data_in['C'] * 100),
                           int( U_data_out['A']* 100), int(U_data_out['B'] * 100),int(U_data_out['C'] * 100),
                           int(I_data_out['A'] * 1000), int(I_data_out['B']* 1000), int(I_data_out['C'] * 1000)]
        except:
            pass
        temperature_duration_time,temperature_start_time = reckon_by_time(temperature_start_time)
        if temperature_duration_time >=2:
            DHT11.measure()
            temperature_duration_time=0
            temperature_start_time=0
            temperature_data=int(DHT11.temperature()*10)
        screen_data= host.read_holding_registers(slave_addr=2, starting_addr=0, register_qty=8)
        if screen_data[0]==1:
            bypass_Control_close_A.value(0)
            bypass_Control_open_A.value(1)
        else:
            bypass_Control_close_A.value(1)
            bypass_Control_open_A.value(0)
        if screen_data[1]==1:
            bypass_Control_close_B.value(0)
            bypass_Control_open_B.value(1)
        else:
            bypass_Control_close_B.value(1)
            bypass_Control_open_B.value(0)
        if screen_data[2]==1:
            bypass_Control_close_C.value(0)
            bypass_Control_open_C.value(1)
        else:
            bypass_Control_close_C.value(1)
            bypass_Control_open_C.value(0)
        if screen_data[3]==1:
            thyristor_control_A.duty(1023)
        else:
            thyristor_control_A.duty(0)
        if screen_data[4]==1:
            thyristor_control_B.duty(1023)
        else:
            thyristor_control_B.duty(0)
        if screen_data[5]==1:
            thyristor_control_C.duty(1023)
        else:
            thyristor_control_C.duty(0)
        if screen_data[6] ==1:
            fan.value(1)
        else:
            fan.value(0)
        if screen_data[7] ==1:
            IC1.calibration()
            IC2.calibration()
            seting_HT7036 =1
        utime.sleep_ms(100)
        append_list=[temperature_data,seting_HT7036]
        screen_data_update.append(append_list)
        host.write_multiple_registers(slave_addr=2, starting_address=8, register_values=screen_data_update)
        utime.sleep_ms(100)
        wtd.feed()
async def protect():
    global U_data_in, I_data_in, U_data_out, I_data_out
    while True:
        U_data_in, I_data_in, U_data_out, I_data_out = ac_sampling()
        pid.electricity_update(U_data_in, I_data_in, U_data_out, I_data_out)
        for phase in ['A', 'B', 'C']:
            pid.Thyristor_protection_function(phase,pid.pwm_out)
        #print(f'A进线电压:{U_data_in['A']},A出线电压:{U_data_out['A']},A出线电流:{I_data_out['A']},PWM{pid.pwm_out['A']},过压次数{pid.over_voltage_times[phase]},过压时间{pid.over_voltage_times_duration[phase]}')
        wtd.feed()
        await uasyncio.sleep_ms(20)
async def run():
    global U_data_in, I_data_in, U_data_out, I_data_out,adjust_times,unbalanced_voltage_1_duration,unbalanced_voltage_1_start_time\
    ,unbalanced_voltage_restore_duration,unbalanced_voltage_restore_start_time,unbalanced_voltage_2_duration,unbalanced_voltage_2_start_time
    while True:
        if pid.mode==0:
            if all(pid.hardware_failure[key] !={6, 7}for key in ['A', 'B', 'C']):
                pid.point = pid.U_resection
                if max(U_data_in.values()) >= 260:
                    unbalanced_voltage_1_duration, unbalanced_voltage_1_start_time = reckon_by_time(
                        unbalanced_voltage_1_start_time)
                    if unbalanced_voltage_1_duration >= 5:
                        max_key_U_in_1 = max(U_data_in, key=U_data_in.get)
                        pid.hardware_failure[max_key_U_in_1] = 6
                        pid.point = 210
                else:
                    unbalanced_voltage_1_duration = 0
                    unbalanced_voltage_1_start_time = 0
                if max(U_data_in.values()) >= 270:
                    unbalanced_voltage_2_duration, unbalanced_voltage_2_start_time = reckon_by_time(
                        unbalanced_voltage_2_start_time)
                    if unbalanced_voltage_2_duration >= 3:
                        for i in ['A', 'B', 'C']:
                            pid.hardware_failure[i] = 7
                            pid.err_states[i]['err'] = 1
                else:
                    unbalanced_voltage_2_duration = 0
                    unbalanced_voltage_2_start_time = 0
            if max(U_data_in.values()) <= 250:
                unbalanced_voltage_restore_duration, unbalanced_voltage_restore_start_time = reckon_by_time(
                    unbalanced_voltage_restore_start_time)
                if unbalanced_voltage_restore_duration >= 3 and any(
                        pid.hardware_failure[key] in {6, 7} for key in ['A', 'B', 'C']):
                    for i in ['A', 'B', 'C']:
                        pid.hardware_failure[i] = 0
                        pid.err_states[i]['err'] = 0
                    pid.point = pid.U_resection
            else:
                unbalanced_voltage_restore_duration = 0
                unbalanced_voltage_restore_start_time = 0
        else:
            if 245 > max(U_data_in.values()) > pid.U_resection:
                pid.point = max(U_data_in.values())
            elif pid.U_resection > (max(U_data_in.values())) > pid.U_input:
                pid.point = max(U_data_in.values())
            else:
                pid.point = pid.U_resection
            if max(pid.pwm_out.values()) > 1020:
                max_key = max(pid.pwm_out, key=pid.pwm_out.get)
                if U_data_out[max_key] < pid.U_resection:
                    pid.point = abs(sum(U_data_out.values()) / len(U_data_out))
                else:
                    pid.point = pid.U_resection
            # print('电机模式', pid.point)
        for phase in ['A', 'B', 'C']:
            pid.pwm_output_funtion(U_data_in, U_data_out, phase)
            Controls(pid.gear_position, pid.pwm_out, phase)
            # print(f'{phase}相进线{U_data_in[phase]}，出线{U_data_out[phase]}，电流{I_data_out[phase]}，PWM{pid.pwm_out[phase]},目标值{pid.point}')
        pid.maintenance_switching()
#         print(f'旁路状态{pid.maintenance_status}，旁路磁保持{pid.gear_position}，硬件故障{pid.hardware_failure},错误提示{pid.error_prompt}')
        wtd.feed()
        await uasyncio.sleep_ms(100)
async def screen():
    global temperature_duration_time, temperature_start_time, reporting_duration_time, reporting_start_time,FJ,FirmwareUpdater_data,\
        updata_duration,updata_start_time,UP_data,modbus_disconnection
    while True:
        # try:
        screen_data = host.read_holding_registers(slave_addr=2, starting_addr=0, register_qty=28)
        pid.Screen_setting_values(screen_data)
        await uasyncio.sleep_ms(100)
        host.write_multiple_registers(slave_addr=2, starting_address=28, register_values=pid.Screen_send_values(version,modbus_disconnection))
        await uasyncio.sleep_ms(100)
        if uart0.any():
            rcv_data = uart0.read()
            print(rcv_data)
            data = updater.Find_Str(rcv_data,b'SOCKET',b'command_name')
            if len(data) > 40:
                Synchronized_reply, escaped_json_string, screen_data_could,UP_data= pid.setting_cloud(data)
                uart0.write(Synchronized_reply)
                print('答应数据',Synchronized_reply)
                await uasyncio.sleep_ms(200)
                host.write_multiple_registers(slave_addr=2, starting_address=0,
                                              register_values=screen_data_could)
                await uasyncio.sleep_ms(200)
                uart0.write(escaped_json_string)

        temperature_duration_time, temperature_start_time = reckon_by_time(temperature_start_time)
        reporting_duration_time, reporting_start_time = reckon_by_time(reporting_start_time)
        if temperature_duration_time >= 60:
            DHT11.measure()
            temperature_duration_time = 0
            temperature_start_time = 0
            pid.fan_control_function(DHT11.temperature())
            fan.value(pid.fan_control)
        print('准备上送数据',reporting_duration_time, pid.report_time)
        if reporting_duration_time >=pid.report_time:
            uart0.write(pid.Parameter_reporting(version,modbus_disconnection))
            reporting_duration_time = 0
            reporting_start_time = 0

        if UP_data:
            updata_duration, updata_start_time =reckon_by_time(updata_start_time)
            if updata_duration < 5:
                if pid.maintenance_status==7:
                    updater.update_task(UP_data)
                    pid.UPDATA=0
            else:
                UP_data=b''
                updata_duration=0
                updata_start_time=0
        # except:
        #     pass
        try:
            input_data=host.read_input_registers(slave_addr=3,starting_addr=0,register_qty=5,signed=False)
            pid.modbus_input(input_data)
            await uasyncio.sleep_ms(100)
            host.write_multiple_registers(slave_addr=3, starting_address=0, register_values=pid.modbus_output())
            await uasyncio.sleep_ms(100)
            modbus_disconnection=0
        except Exception as e:
            modbus_disconnection=1
            await uasyncio.sleep(3)
        wtd.feed()
        await uasyncio.sleep_ms(100)

# async def modbus():
#     global modbus_disconnection
#     while True:
#         try:
#             print('控制通信')
#             input_data=host.read_input_registers(slave_addr=3,starting_addr=0,register_qty=5,signed=False)
#             pid.modbus_input(input_data)
#             await uasyncio.sleep_ms(100)
#             host.write_multiple_registers(slave_addr=3, starting_address=0, register_values=pid.modbus_output())
#             await uasyncio.sleep_ms(100)
#             modbus_disconnection=0
#         except Exception as e:
#             modbus_disconnection=1
#             await uasyncio.sleep(3)
async def main():
    # 创建并启动协程
    tasks = [
        uasyncio.create_task(protect()),
        uasyncio.create_task(run()),
        # uasyncio.create_task(modbus()),
        uasyncio.create_task(screen())
    ]
    # 等待所有协程完成（实际上这里不会完成，因为是无限循环）
    await uasyncio.gather(*tasks)
if __name__ == '__main__':
    IC1 = HT7036(cs1, spi, 'IC1.json')
    IC2 = HT7036(cs2, spi, 'IC2.json')
    wtd = WDT(timeout=90000)
    updater = FirmwareUpdater(uart0, wtd)
#     try:
    if set_pin.value()==0:
        setting_up()
    else:
        power_on()
        pid = PID(Kp=0.3, Ki=0.01, Kd=0.05,sample_time=100)
        # pid = PID(Kp=0.25, Ki=0.1, Kd=0.05, sample_time=0.01)
        # wtd= WDT(timeout=10000)
        IC1.power_on()
        IC2.power_on()
        try:
            pid.hardware_failure= pid.Data_read()['hardware_failure']
            pid.err_states=pid.Data_read()['err_states']
            for i in ['A', 'B', 'C']:
                if pid.hardware_failure[i]==1 or pid.hardware_failure[i]==4 or pid.hardware_failure[i]==5:
                    pid.maintenance_status=8
        except:
            pass
        utime.sleep(2)
        uasyncio.run(main())
#     except Exception as e:  # 异常字处理
#         log_error(e)  # 写故障日志
#         power_on()
